﻿using Sustainalytics.AwsS3.Client;
using Sustainalytics.ClientDW.Entities;
using Sustainalytics.Entities;
using Sustainalytics.Utils.Configuration;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks.Dataflow;

namespace Sustainalytics.ReportGenerator.BL

{
    public class TearSheetReportsManager
    {
        private readonly int _bulkSize;
        private readonly IReadOnlyDictionary<string, string> _connectionStrings;
        private readonly string[] _accountNames;
        private readonly ConcurrentQueue<string> _log;
        private int _reportsCount;
        private int _successfullyGeneratedReports;
        private  readonly IS3Client _s3Client;
        private CancellationTokenSource _tokenSource;


        public string[] Log => _log.ToArray();
        public int ReportsCount => _reportsCount;
        public int SuccessfullyGeneratedReports => _successfullyGeneratedReports;
        private readonly string _bucketName;

        public TearSheetReportsManager()
        {
            _log = new ConcurrentQueue<string>();
        }

        public TearSheetReportsManager(int bulkSize, IReadOnlyDictionary<string, string> connectionStrings, string[] accountNames, IS3Client s3Client)
        {
            _bulkSize = bulkSize;
            _log = new ConcurrentQueue<string>();

            if (!connectionStrings.ContainsKey("ClientPlatform"))
                throw new ArgumentException("ClientPlatform key is missing!");

            if (!connectionStrings.ContainsKey("ClientUniverses"))
                throw new ArgumentException("ClientUniverses key is missing!");

            if (!connectionStrings.ContainsKey("ClientDW"))
                throw new ArgumentException("ClientDW key is missing!");

            _connectionStrings = connectionStrings;
            _accountNames = accountNames;

            _bucketName = SettingsHelper.Read("s3bucket", "dev-globalaccess-europe");

            _s3Client = s3Client;

        }

        private DatabaseGroup SetDatabaseGroup()
        {
            var databaseGroup = DatabaseGroup.None;

            if (_connectionStrings["ClientDW"].Contains("ClientDW_A"))
                databaseGroup = DatabaseGroup.A;
            if (_connectionStrings["ClientDW"].Contains("ClientDW_B"))
                databaseGroup = DatabaseGroup.B;

            return databaseGroup;
        }

        public static TearSheetReportsManager GetInstance(int bulkSize, Dictionary<string, string> connectionStrings, string[] accountNames, IS3Client s3Client)
        {
            return new TearSheetReportsManager(bulkSize, connectionStrings, accountNames, s3Client);
        }

        private void GenerateReport(TearSheetReportDataSource dataSource)
        {
            try
            {

                var clientReportPdfRenderer = new TearSheetReportGenerator(dataSource, false);
                var pdfDocument = clientReportPdfRenderer.GetPdfDocument();
                var fileName = $"{dataSource.CorporateData.CapitalIqId}.pdf";

                var pdfReport = new RenderResult
                {
                    Id = Guid.NewGuid(),
                    ReportType = ReportType.TearSheetReport,
                    CompanyId = dataSource.CompanyId,
                    FileName = fileName,
                    FileType = FileType.Pdf,
                    FileContent = pdfDocument
                };

                SavePdfReport(pdfReport);

                Interlocked.Increment(ref _successfullyGeneratedReports);

                _log.Enqueue(DateTime.UtcNow.ToString("T")+$":{dataSource.CorporateData.CompanyName} TearSheet report was generated and saved successfully. Company Id: {dataSource.CorporateData.Id}");
                Console.WriteLine(DateTime.UtcNow.ToString("T")+$":{dataSource.CorporateData.CompanyName} TearSheet report was generated and saved successfully. Company Id: {dataSource.CorporateData.Id}");
            }
            catch (Exception ex)
            {
                var companyName = dataSource.CorporateData == null ? "No name" : dataSource.CorporateData.CompanyName;
                _log.Enqueue(DateTime.UtcNow.ToString("T")+$":Failed to generate TearSheet report for company {companyName} with ID: {dataSource.CompanyId}. Error: {ex}");
                Console.WriteLine(DateTime.UtcNow.ToString("T")+$":Failed to generate TearSheet report for company {companyName} with ID: {dataSource.CompanyId}. Error: {ex}");
            }
        }

        private void SavePdfReport(RenderResult renderResult)
        {
            if (renderResult == null) return;

            try
            {
                var path = $"{ReportType.TearSheetReport}_{SetDatabaseGroup()}/{renderResult.FileName}";

                _log.Enqueue(DateTime.UtcNow.ToString("T")+$":Uploading file {path} in S3 {_bucketName} bucket");

                 _s3Client.UploadFile(_bucketName, path, renderResult.FileContent);

                renderResult.FileContent = null;
            }
            catch (Exception ex)
            {
                _log.Enqueue(DateTime.UtcNow.ToString("T")+$":Failed to uploading file {renderResult.FileName} in S3 {_bucketName} bucket");
                Console.WriteLine(ex);
            }
        }

        private void LoadCompanyReportData(ITargetBlock<TearSheetReportDataSource> target)
        {
            var reportDataProvider = new TearSheetReportDataProvider(_bulkSize, _tokenSource.Token, _connectionStrings, _accountNames);
            reportDataProvider.OneTimeDataRead();

            _reportsCount = reportDataProvider.CompanyCount;
            _successfullyGeneratedReports = 0;

            var totalObjectsRetrieved = 0;


            while (true)
            {
                var dataSourceObjects = reportDataProvider.GetCompanyReportData(Guid.Empty);

                if (dataSourceObjects.Length <= 0) break;

                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine(DateTime.UtcNow.ToString("T")+$":{totalObjectsRetrieved} total reports done, next batch of {dataSourceObjects.Length} objects retrieved.");
                Console.ResetColor();
                totalObjectsRetrieved += dataSourceObjects.Length;

                foreach (var ds in dataSourceObjects)
                {
                    target.SendAsync(ds).Wait();
                }
            }
        }

        public void GenerateBulkPdfReports()
        {
            _tokenSource = new CancellationTokenSource();

            var processorCount = Environment.ProcessorCount;
            var maxDegreeOfParallelism = (int)Math.Floor(processorCount * 0.75);
            var boundedCapacity = processorCount * 2;

            var bufferBlock = new BufferBlock<TearSheetReportDataSource>(new DataflowBlockOptions
                {
                    BoundedCapacity = boundedCapacity,
                    CancellationToken = _tokenSource.Token
                });

            var generator = new ActionBlock<TearSheetReportDataSource>(dataSource => GenerateReport(dataSource),
                new ExecutionDataflowBlockOptions
                {
                    BoundedCapacity = boundedCapacity,
                    MaxDegreeOfParallelism = maxDegreeOfParallelism,
                    CancellationToken = _tokenSource.Token
                });

            bufferBlock.LinkTo(generator, new DataflowLinkOptions { PropagateCompletion = true });
            bufferBlock.Completion.ContinueWith(task =>
            {
                Console.WriteLine(DateTime.UtcNow.ToString("T") + $":The status of the completion task is '{task.Status}'.");
            });


            LoadCompanyReportData(bufferBlock);

            bufferBlock.Complete();
            generator.Completion.Wait();
        }
    }
}
